import numpy
import collections
import copy
import math

#TODO: it is slow -> replace with quicker one (probably sorted)
class ListSet(collections.Set):
    def __init__(self, iterable):
        self.elements = lst = []
        for value in iterable:
            if value not in lst:
                lst.append(value)
    def __len__(self):
        return len(self.elements)
    def __iter__(self):
        return iter(self.elements)
    def __contains__(self, value):
        return value in self.elements
    def add(self, value):
        if value not in self.elements:
            self.elements.append(value)
    def __str__(self):
        return self.elements.__str__()
  
def influence_window(radius):
    r2 = radius*radius
    point = (int(numpy.floor(radius)), 0)
    
    cond =  lambda (x,y): x*x+y*y<=r2
    counterpart = lambda (x,y): (-y,x) 
    
    set1 = ListSet([]) 
    
    while point[0] <> 0:
        if cond(point):
            set1.add(point)
            point = (point[0], point[1] + 1)
        else:
            point = (point[0], point[1] - 1)
            for y in range(0,point[1]):
                set1.add((point[0], y))
            point = (point[0] - 1, point[1])
            
    
    set2 = ListSet([])
 
    for p in set1:
        set2.add(counterpart(p))
    
    set3 = ListSet([])
    
    for p in set2:
        set3.add(counterpart(p))
    
    set4 = ListSet([])
    
    for p in set3:
        set4.add(counterpart(p))
 
    return set1 | set2 | set3 | set4

#Mean of x = 0
#Mean of y = 0
#Variance of x = Variance of y = variance
def gauss_2d((x, y), variance):
    den = 2.5066282746310002
    den *= variance
    nom = math.exp((-x * x - y * y)/(2 * variance * variance))
    return nom/den


def invoke(obj, func, args):
    if obj == None:
        return None
    f = "obj." + func.__name__ + '('
    args_count = func.func_code.co_argcount - 1
    for i in range(args_count):
        f += 'args[' + str(i) + ']'
        if i != args_count-1:
            f += ','
    f += ')'
    return eval(f) 

def rotate_matrix(axis, angle):
    z = numpy.matrix([0, 0, 1], dtype=numpy.float)
    u = numpy.matrix(axis, copy=True, dtype=numpy.float)
    u /= numpy.linalg.norm(u)
    n = numpy.cross(z, u)
    if numpy.all(n == 0):
        n = numpy.matrix([0, 1, 0], dtype=numpy.float)
    else:        
        n /= numpy.linalg.norm(n)
        
    v = numpy.cross(u, n)
    v /= numpy.linalg.norm(v)
    
    R = numpy.matrix([[u[0,0], u[0,1], u[0,2]],[n[0,0], n[0,1], n[0,2]],[v[0,0], v[0,1], v[0,2]]], dtype=float)

    sina = numpy.sin(angle * numpy.pi / 180.0)
    cosa = numpy.cos(angle * numpy.pi / 180.0)
    Rx = numpy.matrix([[1, 0, 0],[0, cosa, -sina],[0, sina, cosa]], dtype=float)
    
    return numpy.transpose(R) * Rx * R 